/*
 * @module lang
 * @author zfkun@msn.com
 */
;(function(Z) {

    var L = Z.namespace('Lang'),

        OP = Object.prototype,
        AP = Array.prototype,
        SP = String.prototype,
        
        toString = OP.toString,
        slice = AP.slice,
        indexOf = AP.indexOf,
        lastIndexOf = AP.lastIndexOf,
        filter = AP.filter,
        map = AP.map,
        some = AP.some,
        every = AP.every,
        
        trim = SP.trim,
        //clean = SP.clean,
        //substitute = SP.substitute,
        
        typeNameCache = {};
    
    // Converts array-like collection such as LiveNodeList to normal array.
    var obj2Arr = function(obj) {
        return slice.call(obj);
    };

    // IE will throw error.
    try {
        obj2Arr(document.childNodes);
    } catch(e) {
        obj2Arr = function(obj) {
            for (var ret = [], i = 0, n = obj.length; i < n; i++) {
                ret[i] = obj[i];
            }
            return ret;
        }
    }


    Z.extend(L, {

        type : function(o) {
            return o == null ? String(o) : typeNameCache[toString.call(o)] || 'object';
        },
        
        /**
         * Checks to see if an object is a plain object (created using "{}"
         * or "new Object()" or "new FunctionClass()").
         * Ref: http://lifesinger.org/blog/2010/12/thinking-of-isplainobject/
         */
        isPlainObject : function(o) {
            return o && toString.call(o) === '[object Object]' && 'isPrototypeOf' in o;
        },
        
        isEmptyObject : function(o) {
            var i;
            for (i in o) {
                return false;
            }
            return true;
        },
        
        isUndefined : function(o) {
            return o === undefined;
        },
        
        isNull : function(o) {
            return o === null;
        },
        
        isNaN : function(o) {
            return o == null || !/\d/.test(o) || isNaN(o);
        },
        
        inArray : function(item, arr) {
            return L.indexOf(item, arr) > -1;
        },
        
        indexOf : indexOf ? function(item, arr) {
            return indexOf.call(arr, item);
        } : function(item, arr) {
            for (var i = 0, l = arr.length; i < l; i++) {
                if (arr[i] == item) {
                    return i;
                }
            }
            return -1;
        },
        
        lastIndexOf : lastIndexOf ? function(item, arr) {
            return lastIndexOf.call(arr, item);
        } : function(item, arr) {
            for (var i = arr.length - 1; i > 0; i++) {
                if (arr[i] == item) {
                    return i;
                }
            }
            return -1;
        },
        
        each : function(object, fn, scope) {
            var key, val, i = 0, length = object.length,
                isObj = length === undefined || L.type(object) === 'function';

            scope = scope || window;

            if (isObj) {
                for (key in object) {
                    if (fn.call(scope, object[key], key, object) === false) {
                        break;
                    }
                }
            } else {
                for (i = 0; i < length; i++) {
                    val = object[i];
                    if (fn.call(scope, val, i, object) === false) {
                        break;
                    }
                }
            }

            return object;
        },
        
        toArray : function(object) {
            if (object === null || object === undefined) return [];
            if (L.isArray(object)) return object;

            // The strings and functions also have 'length'
            if (typeof object.length !== 'number' || L.isString(object) || L.isFunction(object)) {
                return [object];
            }

            return obj2Arr(object);
        },
        

        /**
         * Array
         */
        filter : filter ? function(arr, fn, scope) {
            return filter.call(arr, fn, scope);
        } : function(arr, fn, scope) {
            var i = 0, l = arr.length, rs = [], v;

            for (; i < l; i++) {
                v = arr[i];
                if (fn.call(scope || window, v, i, arr)) {
                    rs.push(v);
                }
            }

            return rs;
        },

        map : map ? function(arr, fn, scope) {
            return map.call(arr, fn, scope);
        } : function(arr, fn, scope) {
            var i = 0, l = arr.length, rs = new Array(l), v;

            for (; i < l; i++) {
                rs[i] = fn.call(scope || window, arr[i], i, arr);
            }

            return rs;
        },
        
        some : some ? function(arr, fn, scope) {
            return some.call(arr, fn, scope);
        } : function(arr, fn, scope) {

            for (var i = 0, l = arr.length; i < l; i++) {
                if (fn.call(scope || window, arr[i], i, arr)) {
                    return true;
                }
            }

            return false;
        },
        
        every : every ? function(arr, fn, scope) {
            return every.call(arr, fn, scope);
        } : function(arr, fn, scope) {
            for (var i = 0, l = arr.length; i < l; i++) {
                if (!fn.call(scope || window, arr[i], i, arr)) {
                    return false;
                }
            }

            return true;
        }
    
    });


    var basicTypes = 'Boolean,Number,String,Function,Array,Date,RegExp,Object'.split(','),
        i, l = basicTypes.length;

    for (i = 0; i < l; i++) {
        (function(type, k) {
            k = type.toLowerCase();
            typeNameCache['[object ' + type + ']'] = k;

            L['is' + type] = function(o) {
                return L.type(o) == k && (k == 'number' ? isFinite(o) && !isNaN(o) : true);
            };
        })(basicTypes[i], i);
    }

    // publish to root
    L.each('each type map filter some every isPlainObject isEmptyObject inArray toArray'.split(' '), function(method) {
        Z[method] = L[method];
    });

})(ZF);
